#include "putchar.h"

#include <QDebug>

#include "screen.h"

void PutChar::put(unsigned char ch) {
    if ((ch & 0x70) > 0x10) {
        // Normal char.
        if (ch != (0x80 | ' ')) {
            print(ch);
        } else {
            reverse ^= 0x80;
            print(' ');
            reverse ^= 0x80;
        }
    } else switch (ch) {
    case 1:
        screen->init();
        // Fall through.
    case 2:
        x = 0;
        y = 0;
        fgColor = 1;
        break;
    case 17: cursorDown(); break;
    case 145: cursorUp(); break;
    case 157: cursorLeft(); break;
    case 29: cursorRight(); break;
    case 0x12: reverse = 0x80u; break;  // rvs on
    case 0x92: reverse = 0; break;  // rvs off

    /* Colors. */
    case 0x05: fgColor = 1; break;
    case 0x1c: fgColor = 2; break;
    case 0x1e: fgColor = 5; break;
    case 0x1f: fgColor = 6; break;
    case 0x81: fgColor = 8; break;
    case 0x90: fgColor = 0; break;
    case 0x95: fgColor = 9; break;
    case 0x96: fgColor = 0xa; break;
    case 0x97: fgColor = 0xb; break;
    case 0x98: fgColor = 0xc; break;
    case 0x99: fgColor = 0xd; break;
    case 0x9a: fgColor = 0xe; break;
    case 0x9b: fgColor = 0xf; break;
    case 0x9c: fgColor = 4; break;
    case 0x9e: fgColor = 7; break;
    case 0x9f: fgColor = 3; break;

    case 0xd: // Enter.
        x = 0;
        if (y != 24)
            ++y;
        break;

    case 0x8f: break;  // some trash in some animation

    default:
        qDebug() << "Unhandled character " << (int)ch;
        break;
    }
}

void PutChar::print(unsigned char ch) {
    static const unsigned char screencode[256] = {
        0x80u, 0x81u, 0x82u, 0x83u, 0x84u, 0x85u, 0x86u, 0x87u,
        0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu,
        0x90u, 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u,
        0x98u, 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu,
        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
        0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
        0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
        0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
        0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
        0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
        0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
        0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
        0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
        0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
        0xc0u, 0xc1u, 0xc2u, 0xc3u, 0xc4u, 0xc5u, 0xc6u, 0xc7u,
        0xc8u, 0xc9u, 0xcau, 0xcbu, 0xccu, 0xcdu, 0xceu, 0xcfu,
        0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xd6u, 0xd7u,
        0xd8u, 0xd9u, 0xdau, 0xdbu, 0xdcu, 0xddu, 0xdeu, 0xdfu,
        0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
        0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
        0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
        0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
        0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
        0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
        0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
        0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
        0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
        0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
        0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
        0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0xffu
    };
    screen->setChar(x, y, screencode[ch] ^ reverse);
    screen->setColor(x, y, fgColor);

    if (x != 39)
        cursorRight();
}

void PutChar::cursorDown() {
    if (y != 24)
        ++y;
    else
        screen->moveUp();
}

void PutChar::cursorRight() {
    if (x != 39)
        ++x;
    else
        screen->moveLeft();
}

void PutChar::cursorLeft() {
    if (x)
        --x;
    else
        screen->moveRight();
}

void PutChar::cursorUp() {
    if (y)
        --y;
    else
        screen->moveDown();
}

void PutChar::hideCursor() {
    screen->setColor(x, y, hiddenCursorColor);
    screen->setChar(x, y, hiddenCursorChar);
}

void PutChar::showCursor() {
    hiddenCursorColor = screen->getColor(x, y);
    screen->setColor(x, y, fgColor);
    hiddenCursorChar = screen->getChar(x, y);
    screen->setChar(x, y, 0x80 ^ hiddenCursorChar);
}
